#!/usr/bin/env python
""" AnyBackup is a windows specific backup program to keep multiple volumes in sync with multiple volumes
    Copyright (C) 2011  Andrew Hopkinson

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Exported Classes:

    StoppableThread -- a thread which can be stopped
    myFrame -- the main GUI component of AnyBackup
    """
from FileUtil import FileUtil
from sql import SQLLite
from backupManager import file_cmp
import DriveUtil
from easyListCtrl import easyListCtrl
from dirTree import dirTree
from backupManager import backupManager
from listdialog import listdialog, manageDirDialog, remoteDialog, SearchDialog
from ScrollingProgressDialog import ScrollingProgressDialog
from mySplash import mySplash
import wx
import wx.lib.agw.aui as aui
from wx.lib.agw.multidirdialog import MultiDirDialog
from threading import Thread, Event
from operator import attrgetter
from multiprocessing import Queue
import pythoncom
from enum import AEnums
import re
import shutil
import os
import copy
import time
import cPickle
import sys
import datetime
import Queue as realQueue
import logging
import logging.handlers
import subprocess
import traceback


class StoppableThread (Thread):
    """ a thread which can be stopped """
    def __init__ (self,bucket,target,*args):
        self.bucket = bucket
        self.target = target
        self.args = args
        self.stopEvent = False
        Thread.__init__(self)

    def getObj(self):
        return self.buManager

    def stop (self):
        self.stopEvent = True
        self.join()

    def isDone(self):
        return self.stopEvent

    def run(self):
        pythoncom.CoInitialize()
        os.sys.settrace(self.globaltrace)
        try:
            self.target(*self.args)
        except Exception:
            self.bucket.put(traceback.format_exc())
        finally:
            pythoncom.CoUninitialize()

    def globaltrace(self, frame, why, arg):
        if why == 'call':
            return self.localtrace
        else:
            return None

    def localtrace(self, frame, why, arg):
        if self.stopEvent:
            if why == 'line':
                raise SystemExit()
            return self.localtrace


class AnyFrame(wx.Frame):
    """ the main GUI component of AnyBackup, the whole of the GUI """
    MISSING=9
    _maxResults=300

    def __init__(self, parent, ID, title, position, size):
        wx.Frame.__init__(self,parent,ID,title + ' ' + backupManager.version,position,size)

        self.logger = logging.getLogger('AnyBackup')
        self.formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(funcName)s - %(message)s')
        self.handler = logging.handlers.TimedRotatingFileHandler('AnyBackup.log',when='D',interval=1,backupCount=5)
        self.handler.setFormatter(self.formatter)
        self.logger.addHandler(self.handler)
        self.logger.setLevel(logging.DEBUG)
        self.logger.propagate = False
        self.bucket = realQueue.Queue()
        self.logger.info('Starting AnyBackup')
        self.buManager = backupManager()
        self.messagingQueue = Queue()
        self.errorQueue = Queue()
        self.errorAnswerQueue = Queue()
        splash = mySplash()
        splash.show()

        splash.setStatusText('Building GUI')
        self.mgr = aui.AuiManager()
        self.mgr.SetManagedWindow(self)
        self.bottomPanel = wx.Panel(self, -1, size = (450,150))
        self.drvPanel = wx.Panel(self, -1, size=(350,300))
        resultPanel = wx.Panel(self.bottomPanel)
        drvSelPanel = wx.Panel(self.drvPanel)
        drvListPanel = wx.Panel(self.drvPanel)

        mainBox = wx.BoxSizer(wx.VERTICAL)
        boxTop = wx.BoxSizer(wx.HORIZONTAL)
        boxBottom = wx.BoxSizer(wx.HORIZONTAL)
        driveBox = wx.BoxSizer(wx.VERTICAL)
        drvSelBox = wx.BoxSizer(wx.HORIZONTAL)
        drvListBox = wx.BoxSizer(wx.HORIZONTAL)
        resultBox = wx.BoxSizer(wx.VERTICAL)
        self.saving = False
        self.unsaved = False
        self.ids = {}
        self.sdialog = {}
        self.thread = None
        self.cursor = None
        self.running = False
        keys = ['cnLabel','drives','files','exit','addMenu',
                'backupMenu','removeMenu','toBackupMenu',
                'oldFilesMenu','results','statusBar',
                'editListMenu','save','contents','about',
                'refreshBackup','refreshContent','reset',
                'refreshSelected','search','dirTree',
                'dirLabel','progressBar','clear','restoreMenu',
                'resultPage','toggleMenu','tb_add','tb_remove',
                'tb_refresh','tb_lock','tb_backup','tb_search',
                'tb_switch','duplicates','timer','refreshButton',
                'sets','addset','removeset']
        for key in keys:
            self.ids[key] = wx.NewId()
        self.CreateStatusBar()
        self.GetStatusBar().SetFieldsCount(3)
        self.fileUtilObj = FileUtil()
        self.cnLabel = wx.StaticText(self.drvPanel,self.ids['cnLabel'],"Drives")
        self.cnLegend = wx.StaticText(drvSelPanel,-1,'Content Drive')
        self.cnLegend.SetFont(wx.Font(7,wx.DEFAULT,wx.NORMAL,wx.BOLD))
        self.cnLegend2 = wx.StaticText(drvSelPanel,-1,'Backup Drive')
        self.cnLegend2.SetFont(wx.Font(7,wx.DEFAULT,wx.NORMAL,wx.NORMAL))
        self.refreshButton = wx.BitmapButton(drvSelPanel,self.ids['refreshButton'],wx.Bitmap("icons/refresh.png",wx.BITMAP_TYPE_PNG))
        self.drives = easyListCtrl(drvListPanel,self.ids['drives'],self.fileUtilObj,mode=easyListCtrl.DRIVE,single=False)
        self.drives.SetImageList(self.fileUtilObj.getDriveList(),wx.IMAGE_LIST_SMALL)
        self.dirTreeObj = dirTree(self,self.ids['dirTree'],self.fileUtilObj)
        self.dirTreeObj.SetImageList(self.fileUtilObj.getImageList())
        self.contents = easyListCtrl(self,self.ids['contents'],self.fileUtilObj,mode=easyListCtrl.FILE)
        self.contents.SetImageList(self.fileUtilObj.getImageList(),wx.IMAGE_LIST_SMALL)
        self.results = easyListCtrl(resultPanel,self.ids['results'],self.fileUtilObj,mode=easyListCtrl.FILE,single=False)
        self.results.SetImageList(self.fileUtilObj.getImageList(),wx.IMAGE_LIST_SMALL)
        self.resultPage = wx.ComboBox(resultPanel,self.ids['resultPage'],'',wx.DefaultPosition,wx.DefaultSize,[],style=wx.CB_READONLY)
        resultBox.Add(self.resultPage,0,wx.LEFT|wx.TOP,10)
        resultBox.Add(self.results.getListCtrl(),1,wx.EXPAND|wx.ALL,10)
        resultPanel.SetSizer(resultBox)
        drvSelBox.Add(self.cnLegend,0,wx.LEFT|wx.TOP,8)
        drvSelBox.Add(self.cnLegend2,0,wx.LEFT|wx.TOP,8)
        drvSelBox.Add(self.refreshButton,0,wx.LEFT|wx.TOP,8)
        drvSelPanel.SetSizer(drvSelBox)
        drvListBox.Add(self.drives.getListCtrl(),1,wx.EXPAND|wx.ALL,5)
        drvListPanel.SetSizer(drvListBox)
        driveBox.Add(self.cnLabel,0,wx.LEFT|wx.TOP|wx.RIGHT,8)
        driveBox.Add(drvSelPanel,0)
        driveBox.Add(drvListPanel,1,wx.EXPAND|wx.ALL)
        self.drvPanel.SetSizer(driveBox)
        boxBottom.Add(resultPanel,1,wx.EXPAND|wx.ALL,0)
        self.bottomPanel.SetSizer(boxBottom)
        self.progress = None
        self.Centre()
        self.buManager.setMessagingQueue(self.messagingQueue)
        self.buManager.setErrorQueue(self.errorQueue)
        self.buManager.setErrorAnswerQueue(self.errorAnswerQueue)
        loadResult = None
        splash.setStatusText('Populating Drives')
        self.drives.populateDrives(self.getDrives())
        splash.setStatusText('Building AUI Panels')
        self.mgr.SetDockSizeConstraint(.5,.5)
        self.mgr.AddPane(self.bottomPanel, aui.AuiPaneInfo().Name('Results').Caption('Results').Bottom().Layer(1).MaximizeButton(True).CloseButton(False))
        self.mgr.AddPane(self.drvPanel, aui.AuiPaneInfo().Name('Drives').Caption('Content and Backup Drives').Left().Layer(1).MaximizeButton(True).CloseButton(False))
        self.mgr.AddPane(self.contents.getListCtrl(), aui.AuiPaneInfo().Name('File View').Caption('Files in selected directory').Center().MaximizeButton(True).CloseButton(False))
        self.mgr.AddPane(self.dirTreeObj, aui.AuiPaneInfo().Name('Directory View').Caption('Directory view of selected drive').Left().MaximizeButton(True).CloseButton(False))
        self.mgr.Update()
        toolbar = aui.AuiToolBar(self,-1,wx.DefaultPosition,wx.DefaultSize,agwStyle=aui.AUI_TB_DEFAULT_STYLE | aui.AUI_TB_OVERFLOW | aui.AUI_TB_TEXT | aui.AUI_TB_HORZ_TEXT)
        setbar = aui.AuiToolBar(self,-1,wx.DefaultPosition,size=(1000,-1),agwStyle=aui.AUI_TB_DEFAULT_STYLE | aui.AUI_TB_NO_AUTORESIZE | aui.AUI_TB_OVERFLOW |  aui.AUI_TB_TEXT | aui.AUI_TBTOOL_TEXT_BOTTOM)
        setbar.SetToolBitmapSize(wx.Size(30,30))
        toolbar.SetToolBitmapSize(wx.Size(16,16))
        tb_add = self.getBitmap('icons/add-drive.png')
        self.tb_add_small  = self.getBitmap('icons/add-drive-small.png')
        self.tb_remove_small  = self.getBitmap('icons/remove-drive-small.png')
        tb_remove = self.getBitmap('icons/remove-drive.png')
        tb_refresh = self.getBitmap('icons/refresh-big.png')
        self.tb_refresh_small = self.getBitmap('icons/refresh-small.png')
        tb_lock = self.fileUtilObj.getLockedBMP()
        self.tb_lock_small = self.getBitmap('icons/drivelock_icon_small.png')
        tb_backup = self.getBitmap('icons/backup-icon.png')
        tb_backup_small = self.getBitmap('icons/backup-small.png')
        tb_search = self.getBitmap('icons/search.png')
        tb_search_small = self.getBitmap('icons/search-small.png')
        self.tb_explorer = self.getBitmap('icons/explorer.png')
        self.tb_content = self.getBitmap('icons/content.png')
        self.tb_dirs = self.getBitmap('icons/dirs.png')
        tb_new = self.getBitmap('icons/new.png')
        tb_old = self.getBitmap('icons/old.png')
        tb_clear = self.getBitmap('icons/clear.png')
        tb_restore = self.getBitmap('icons/restore.png')
        tb_edit = self.getBitmap('icons/edit-list.png')
        tb_add_set = self.getBitmap('icons/add-set.png')
        tb_duplicate = self.getBitmap('icons/duplicates.png')
        tb_remove_set = self.getBitmap('icons/remove-set.png')
        tb_exit = self.getBitmap('icons/exit.png')
        tb_about = self.getBitmap('icons/about.png')
        self.tb_drive = self.getBitmap('icons/drive.png')
        self.tb_remote = self.getBitmap('icons/network.png')
        toolbar.AddSimpleTool(self.ids['tb_add'],'Add Drive',tb_add)
        toolbar.SetToolDropDown(self.ids['tb_add'], True)
        toolbar.AddSimpleTool(self.ids['tb_remove'],'Remove Drive',tb_remove)
        toolbar.AddSimpleTool(self.ids['tb_refresh'],'Refresh Drive(s)',tb_refresh)
        toolbar.AddSimpleTool(self.ids['tb_lock'],'Un/Lock',tb_lock)
        toolbar.AddSimpleTool(self.ids['tb_backup'],'Backup',tb_backup)
        toolbar.AddSimpleTool(self.ids['tb_search'],'Search',tb_search)
        self.sets = wx.ComboBox(setbar,self.ids['sets'],choices=self.buManager.getSets(),value=self.buManager._setName,style=wx.CB_READONLY)
        setbar.AddLabel(-1,'Backup Set: ',55)
        setbar.AddControl(self.sets)
        self.mgr.AddPane(toolbar, aui.AuiPaneInfo().ToolbarPane().Top().Row(1).CloseButton(False))
        self.mgr.AddPane(setbar, aui.AuiPaneInfo().ToolbarPane().Top().Row(2).CloseButton(False))
        self.mgr.Update()
        splash.setStatusText('Building Menus')
        file_menu = wx.Menu()
        file_menu_entries = []
        file_menu_entries.append([self.ids['search'],"SQL &Find\tCtrl-F","SQL Find",tb_search_small])
        #file_menu_entries.append([self.ids['reset'],"Reset Layout","Reset Layout"])
        file_menu_entries.append(['-'])
        file_menu_entries.append([self.ids['exit'],"E&xit\tCtrl-X","Exit",tb_exit])
        edit_menu = wx.Menu()
        edit_menu_entries = []
        edit_menu_entries.append([self.ids['clear'],"&Clear All Lists\tCtrl-C","Clear All Lists",tb_clear])
        edit_menu_entries.append(['-'])
        edit_menu_entries.append([self.ids['addMenu'],"&Add Drive\tCtrl-A","Add Drive",self.tb_add_small])
        edit_menu_entries.append([self.ids['removeMenu'],"&Remove Drive\tCtrl-R","Remove Drive",self.tb_remove_small])
        edit_menu_entries.append(['-'])
        edit_menu_entries.append([self.ids['addset'],"Add Set","Add Set",tb_add_set])
        edit_menu_entries.append([self.ids['removeset'],"Remove Set","Remove Set",tb_remove_set])
        edit_menu_entries.append(['-'])
        edit_menu_entries.append([self.ids['editListMenu'],"&Edit Lists\tCtrl-E","Edit Lists",tb_edit])
        edit_menu_entries.append(['-'])
        edit_menu_entries.append([self.ids['toBackupMenu'],"&To Backup\tCtrl-T","Files to backup/delete.",tb_new])
        edit_menu_entries.append([self.ids['oldFilesMenu'],"&Old Files\tCtrl-O","Files to delete/restore.",tb_old])
        edit_menu_entries.append([self.ids['backupMenu'],"&Backup\tCtrl-B","Backup Now",tb_backup_small])
        edit_menu_entries.append([self.ids['restoreMenu'],"Restore","Restore Now",tb_restore])
        edit_menu_entries.append([self.ids['duplicates'],"&Duplicate Files\tCtrl-D","Duplicates Files",tb_duplicate])
        refresh_menu = wx.Menu()
        refresh_menu_entries = []
        refresh_menu_entries.append([self.ids['refreshBackup'],"Refresh Backup Drives","Refresh Backup Drives",self.tb_refresh_small])
        refresh_menu_entries.append([self.ids['refreshContent'],"Refresh Content Drives","Refresh Content Drives",self.tb_refresh_small])
        refresh_menu_entries.append([self.ids['refreshSelected'],"Refresh Selected Drives","Refresh Selected Drive",self.tb_refresh_small])
        help_menu = wx.Menu()
        help_menu_entries = []
        help_menu_entries.append([self.ids['about'],'About','About',tb_about])
        self.buildMenu(file_menu,file_menu_entries)
        self.buildMenu(edit_menu,edit_menu_entries)
        self.buildMenu(refresh_menu,refresh_menu_entries)
        self.buildMenu(help_menu,help_menu_entries)
        menuBar = wx.MenuBar()
        menuBar.Append(file_menu, '&File')
        menuBar.Append(edit_menu, '&Edit')
        menuBar.Append(refresh_menu, '&Refresh')
        menuBar.Append(help_menu,'&Help')
        self.SetMenuBar(menuBar)
        self.SetStatusText("Welcome to AnyBackup!",1)
        splash.setStatusText('Creating Events')
        wx.EVT_LIST_ITEM_SELECTED(self,self.ids['drives'],self.driveClicked)
        wx.EVT_LIST_ITEM_ACTIVATED(self,self.ids['contents'],self.fileClicked)
        wx.EVT_LIST_ITEM_RIGHT_CLICK(self,self.ids['contents'],self.contentRightClick)
        wx.EVT_LIST_ITEM_RIGHT_CLICK(self,self.ids['results'],self.resultsRightClick)
        #wx.EVT_LIST_ITEM_SELECTED(self,self.ids['results'],self.resultFileClicked)
        wx.EVT_LIST_ITEM_RIGHT_CLICK(self,self.ids['drives'],self.driveRightClicked)
        wx.EVT_LIST_ITEM_ACTIVATED(self,self.ids['results'],self.resultsActivated)
        wx.EVT_COMBOBOX(self,self.ids['resultPage'],self.resultPageChanged)
        wx.EVT_MENU(self,self.ids['removeMenu'],self.guiRemoveDrive)
        wx.EVT_MENU(self,self.ids['addset'],self.addSet)
        wx.EVT_MENU(self,self.ids['removeset'],self.removeSet)
        wx.EVT_MENU(self,self.ids['addMenu'],self.guiAddDrive)
        wx.EVT_MENU(self,self.ids['toBackupMenu'],self.guiToBackup)
        wx.EVT_MENU(self,self.ids['oldFilesMenu'],self.guiOldFiles)
        wx.EVT_MENU(self,self.ids['duplicates'],self.guiDuplicates)
        wx.EVT_MENU(self,self.ids['backupMenu'],self.guiBackup)
        wx.EVT_MENU(self,self.ids['restoreMenu'],self.guiRestore)
        wx.EVT_MENU(self,self.ids['exit'],self.exit)
        wx.EVT_MENU(self,self.ids['editListMenu'],self.editLists)
        #wx.EVT_MENU(self,self.ids['save'],self.save)
        wx.EVT_MENU(self,self.ids['refreshBackup'],self.refreshBackup)
        wx.EVT_MENU(self,self.ids['refreshContent'],self.refreshContent)
        wx.EVT_MENU(self,self.ids['refreshSelected'],self.refreshSelected)
        wx.EVT_MENU(self,self.ids['search'],self.search)
        wx.EVT_MENU(self,self.ids['clear'],self.clear)
        wx.EVT_MENU(self,self.ids['about'],self.about)
        #wx.EVT_MENU(self,self.ids['reset'],self.reset)
        wx.EVT_TOOL(self,self.ids['tb_add'],self.createAddDriveMenu)
        wx.EVT_TOOL(self,self.ids['tb_remove'],self.guiRemoveDrive)
        wx.EVT_TOOL(self,self.ids['tb_refresh'],self.refreshSelected)
        wx.EVT_TOOL(self,self.ids['tb_lock'],self.toggleLock)
        wx.EVT_TOOL(self,self.ids['tb_backup'],self.guiBackup)
        wx.EVT_TOOL(self,self.ids['tb_search'],self.search)
        wx.EVT_BUTTON(self,self.ids['refreshButton'],self.refreshDriveList)
        wx.EVT_CLOSE(self,self.exit)
        wx.EVT_TREE_SEL_CHANGED(self,self.ids['dirTree'],self.treeClicked)
        wx.EVT_TREE_ITEM_EXPANDING(self,self.ids['dirTree'],self.treeExpanded)
        wx.EVT_COMBOBOX(self,self.ids['sets'],self.setChange)
        self.buildContentMenu()
        self.buildResultMenu()
        self.SetIcon(self.fileUtilObj.getWindowIcon())
        splash.close()
        self.GetStatusBar().SetStatusWidths([-1,-4,150])
        self.SetStatusText("Backup Mode: " + self.buManager.backupMode,2)
        self.timer = wx.Timer(self,self.ids['timer'])
        self.timer.Start(100)
        wx.EVT_TIMER(self,self.ids['timer'],self.readQueues)
        if loadResult == backupManager.INVALIDCONFIG:
            self.SetStatusText("Invalid configuration file, using defaults!",1)
            configDialog = wx.MessageDialog(None, 'Check AnyBackup.cfg, invalid options defined.', 'Config Error', wx.OK | wx.ICON_ERROR)
            configDialog.ShowModal()

    def getBitmap(self,image):
        return wx.Bitmap(image,wx.BITMAP_TYPE_PNG)
        #start_image = wx.Image(image)
        #start_image.Rescale(24,24)
        #return wx.BitmapFromImage(start_image)

    def setChange(self,evt=None):
        self.buManager.setName(self.sets.GetValue())
        self.refreshDrives()

    def addSet(self,evt):
        d = wx.TextEntryDialog(self,'What is the set name?','New Set','')
        if d.ShowModal() == wx.ID_OK:
            answer = d.GetValue()
            if answer not in self.buManager.getSets():
                self.buManager.addSet(answer)
                self.resetSets()

    def resetSets(self,evt=None):
        self.sets.Clear()
        [self.sets.Append(x) for x in self.buManager.getSets()]
        self.sets.SetValue(self.buManager._setName)

    def removeSet(self,evt):
        set = self.sets.GetValue()
        if len(self.buManager.getSets()) == 1:
            wx.MessageDialog(self,'Cannot remove only set!','Error',style=wx.ICON_EXCLAMATION|wx.OK).ShowModal()
            return
        d = wx.MessageDialog(self,'Are you sure you want to delete set {0}?'.format(set),'Confirm',style=wx.YES_NO|wx.NO_DEFAULT|wx.ICON_QUESTION)
        if d.ShowModal() == wx.ID_YES:
            self.buManager.removeSet(set)
            self.resetSets()

    def getPosition(self):
        return  self.ScreenToClient(wx.GetMousePosition())

    def contentRightClick(self,evt):
        self.PopupMenu(self.contentMenu,self.getPosition())

    def resultsRightClick(self,evt):
        self.PopupMenu(self.resultMenu,self.getPosition())

    def resultsActivated(self,evt):
        fileRef = self.results.GetSelectedData()
        self.showInExplorer(fileRef)

    def buildContentMenu(self):
        self.contentMenu = wx.Menu()
        showInExplorer = wx.MenuItem(self.contentMenu,-1,'Show In Explorer')
        showInExplorer.SetBitmap(self.tb_explorer)
        self.contentMenu.AppendItem(showInExplorer)
        self.contentMenu.Bind(wx.EVT_MENU,self.contentShowInExplorer,showInExplorer)

    def buildResultMenu(self):
        self.resultMenu = wx.Menu()
        showInExplorer = wx.MenuItem(self.resultMenu,-1,'Show In Explorer')
        showInExplorer.SetBitmap(self.tb_explorer)
        showInContent = wx.MenuItem(self.resultMenu,-1,'Show In Content Panel')
        showInContent.SetBitmap(self.tb_content)
        self.resultMenu.AppendItem(showInExplorer)
        self.resultMenu.AppendItem(showInContent)
        self.resultMenu.Bind(wx.EVT_MENU,self.resultShowInExplorer,showInExplorer)
        self.resultMenu.Bind(wx.EVT_MENU,self.resultFileClicked,showInContent)

    def resultShowInExplorer(self,evt):
        fileRef = self.results.GetSelectedData()
        self.showInExplorer(fileRef)

    def contentShowInExplorer(self,evt):
        fileRef = self.contents.GetSelectedData()
        self.showInExplorer(fileRef)

    def showInExplorer(self,fileRef):
        if type(fileRef) == list:
            fileRef = fileRef[0]
        if type(fileRef) == str:
            return
        path = self.buManager.getFullPath(fileRef)
        if path:
            if os.path.exists(path):
                subprocess.Popen(r'explorer /select,"{0}"'.format(path))

    def buildMenu(self,menu,entries):
        for entry in entries:
            if entry[0] == '-':
                menu.AppendSeparator()
            else:
                mi = wx.MenuItem(menu,entry[0],entry[1],entry[2])
                if len(entry) > 3:
                    mi.SetBitmap(entry[3])
                menu.AppendItem(mi)

    def about(self,evt):
        info = wx.AboutDialogInfo()
        info.SetIcon(wx.Icon("icons/windowlogo.png",wx.BITMAP_TYPE_PNG))
        info.SetName('AnyBackup')
        info.SetVersion(self.buManager.version)
        info.SetDescription('''AnyBackup is designed to fill the void of backing up one
        or more large volumes to several smaller volumes.
        (Though in reality it can backup any number / sized
        volumes to any number / sized volumes.) These smaller volumes
        need not be persistent or always connected (that is,
        AnyBackup will recognize the drives even if
        their drive letters change).''')
        info.SetCopyright('(C) 2012 Andrew Hopkinson')
        info.SetWebSite('http://code.google.com/p/anybackup')
        info.SetLicence('''AnyBackup is a windows specific backup program to keep multiple volumes in sync with multiple volumes
    Copyright (C) 2011  Andrew Hopkinson

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.''')
        info.AddDeveloper('Andrew Hopkinson')
        info.AddDocWriter('Andrew Hopkinson')
        wx.AboutBox(info)


    def readQueues(self,evt):
        self.readStatusQueue()
        self.readErrorQueue()

    def refreshDriveList(self,evt):
        self.drives.DeleteAllItems()
        self.drives.populateDrives(self.getDrives())

    def readStatusQueue(self):
        while not self.messagingQueue.empty():
            message = self.messagingQueue.get_nowait()
            if message:
                self.SetStatusText(unicode(message).encode('utf-8'),1)

    def readErrorQueue(self):
        while not self.errorQueue.empty():
            message = self.errorQueue.get_nowait()
            if message:
                self.errorAnswerQueue.put(self.missingDrive(message),1)

    def createAddDriveMenu(self,evt):
        tb = evt.GetEventObject()
        tb.SetToolSticky(evt.GetId(), True)
        menuPopup = wx.Menu()
        tb_add = wx.ArtProvider.GetBitmap(wx.ART_HARDDISK, wx.ART_OTHER, wx.Size(16, 16))
        m1 = wx.MenuItem(menuPopup,-1,'Add Backup Drive')
        m1.SetBitmap(tb_add)
        menuPopup.AppendItem(m1)

        m2 = wx.MenuItem(menuPopup,-1,'Add Content Drive')
        m2.SetBitmap(tb_add)
        menuPopup.AppendItem(m2)

        rect = tb.GetToolRect(evt.GetId())
        pt = tb.ClientToScreen(rect.GetBottomLeft())
        pt = self.ScreenToClient(pt)
        self.Bind(wx.EVT_MENU,self.guiAddBackupDrive,m1)
        self.Bind(wx.EVT_MENU,self.guiAddContentDrive,m2)
        self.PopupMenu(menuPopup,pt)
        tb.SetToolSticky(evt.GetId(),False)


    def driveRightClicked(self,evt):
        menu = self.createMenu()
        self.PopupMenu(menu,evt.GetPoint())

    def isBackupMode(self):
        driveRefs = self.drives.GetSelectedData()
        return self.buManager.isBackupMode(driveRefs)

    def isContentMode(self):
        driveRefs = self.drives.GetSelectedData()
        return not self.buManager.isBackupMode(driveRefs)

    def getDrives(self):
        return self.buManager.getBackupDrives()+self.buManager.getContentDrives()

    def toggleLock(self,evt):
        if self.running:
            return
        self.running = True
        driveRefs = self.drives.GetSelectedData()
        if not driveRefs:
            self.running = False
            return
        self.buManager.toggleLock(driveRefs)
        self.refreshDrives()
        self.running = False

    def createMenu(self):
        menu = wx.Menu()
        driveRefs = self.drives.GetSelectedData()
        editDir = None
        if len(driveRefs) == 1:
            d = driveRefs[0]
            if not self.buManager.isBackupMode(driveRefs[0:1]):
                disp = wx.MenuItem(menu,-1,'Content Drive - %s'%d[AEnums.NAME])
                disp.SetBitmap(self.tb_drive)
                disp.Enable(False)
                menu.AppendItem(disp)
                editDir = wx.MenuItem(menu,-1,'Manage Directories')
                editDir.SetBitmap(self.tb_dirs)
                menu.AppendItem(editDir)
            else:
                disp = wx.MenuItem(menu,-1,'Backup Drive - %s'%d[AEnums.NAME])
                disp.SetBitmap(self.tb_drive)
                disp.Enable(False)
                menu.AppendItem(disp)
            menu.AppendSeparator()
        if self.isBackupMode():
            allLocked = True
            for driveRef in driveRefs:
                if not self.buManager.isLocked(driveRef):
                    allLocked = False
                    break
            if allLocked:
                lock = wx.MenuItem(menu,-1,'Unlock')
            else:
                lock = wx.MenuItem(menu,-1,'Lock')
            lock.SetBitmap(self.tb_lock_small)
            menu.AppendItem(lock)
        elif self.isContentMode():
            remoteIndex = wx.MenuItem(menu,-1,'Remote Path')
            remoteIndex.SetBitmap(self.tb_remote)
            menu.AppendItem(remoteIndex)
        remove = wx.MenuItem(menu,-1,'Remove')
        remove.SetBitmap(self.tb_remove_small)
        refresh = wx.MenuItem(menu,-1,'Refresh')
        refresh.SetBitmap(self.tb_refresh_small)
        menu.AppendItem(remove)
        menu.AppendItem(refresh)
        #toggleDrive = menu.Append(-1,'Toggle Backup/Content')
        self.Bind(wx.EVT_MENU,self.guiRemoveDrive,remove)
        self.Bind(wx.EVT_MENU,self.refreshSelected,refresh)
        if editDir:
            self.Bind(wx.EVT_MENU,self.editDirs,editDir)
        if self.isBackupMode():
            self.Bind(wx.EVT_MENU,self.toggleLock,lock)
        elif self.isContentMode():
            self.Bind(wx.EVT_MENU,self.remoteIndex,remoteIndex)
        #self.Bind(wx.EVT_MENU,self.toggleDrives,toggleDrive)
        return menu

    def editDirs(self,evt):
        if self.running:
            return
        driveRefs = self.drives.GetSelectedData()
        if len(driveRefs) > 1:
            dialog = wx.MessageBox(self,"Multiple Selections","You can only select one drive at a time for remote indexing!",style=wx.OK)
            return
        if not driveRefs:
            return
        driveRef = driveRefs[0]
        dialog = manageDirDialog(self,driveRef)
        dialog.ShowModal()

    def toggleDrives(self,event):
        if self.running:
            return
        else:
            self.running = True
        driveRefs = self.drives.GetSelectedData()
        if not driveRefs:
            return
        for driveRef in driveRefs:
            self.buManager.toggleDrive(driveRef)
        self.refreshDrives()
        self.running = False

    def remoteIndex(self,event):
        if self.running:
            return
        driveRefs = self.drives.GetSelectedData()
        if len(driveRefs) > 1:
            dialog = wx.MessageBox(self,"Multiple Selections","You can only select one drive at a time for remote indexing!",style=wx.OK)
            return
        if not driveRefs:
            return
        driveRef = driveRefs[0]
        remoteDialog(self,driveRef).ShowModal()
        #dialog = wx.TextEntryDialog(self,"Enter hostname or IP addresss","Remote Index", '', style=wx.OK|wx.CANCEL)
        #if dialog.ShowModal() == wx.ID_CANCEL:
            #return
        #else:
            #host = dialog.GetValue()
        #pathDialog = wx.TextEntryDialog(self,"Enter remote path","Remote Index",'', style=wx.OK|wx.CANCEL)
        #if pathDialog.ShowModal() == wx.ID_CANCEL:
            #return
        #else:
            #path = pathDialog.GetValue()
        #driveRef.setRemoteHost(host)
        #driveRef.setRemotePath(path)

    def threadedRemoteIndex(self,drive):
        errorMessages = []
        if not self.buManager.remoteIndex(drive):
            errorMessages.append("Remote indexing error!")
        wx.CallAfter(self.refreshDone,errorMessages)
        os.sys.exit(0)

    def threadError(self,event=None):
        if self.running:
            self.idle()
        try:
            exc_trace = self.bucket.get(block=False)
        except realQueue.Empty:
            pass
        else:
            # deal with the exception
            exc_trace = str(exc_trace)
            wx.MessageDialog(self, exc_trace, 'Error', wx.OK | wx.ICON_ERROR).ShowModal()
            self.SetStatusText('Error!',1)
            self.logger.error(exc_trace)

    def search(self,event):
        if self.running:
            return
        #regex = None
        #dialog = wx.TextEntryDialog(self,"Search","Enter SQL expression", '', style=wx.OK|wx.CANCEL)
        if self.buManager.getSetName() not in self.sdialog:
            self.sdialog[self.buManager.getSetName()] = SearchDialog(self)
        d = self.sdialog[self.buManager.getSetName()]
        if d.ShowModal() == wx.ID_CANCEL:
            return
        else:
            regex = d.GetValue()
        #self.SetStatusText("Searching for " + regex + "...", 1)
        self.SetStatusText("Searching for " + regex[AEnums.QUERY] + "...", 1)
        self.thread = StoppableThread(self.bucket,self.threadedSearch, regex)
        self.thread.start()
        self.busy('Searching')

    def clear(self,event):
        self.results.DeleteAllItems()
        self.typeChanged(event)

    def resultPageChanged(self,event):
        results = event.GetClientData()
        self.results.setMode(easyListCtrl.FILE)
        self.results.populateFiles(results,None,True)
        newItem = wx.ListItem()
        newItem.SetText("Over "+str(self._maxResults)+" search results.")
        newItem.SetTextColour(wx.RED)
        newItem.SetImage(self.fileUtilObj.getError())
        self.results.InsertItem(newItem)
        self.results.adjust()

    def paginateResults(self,results):
        lowerBound = 0
        upperBound = self._maxResults - 1
        self.resultPage.Clear()
        quit = False
        while not quit:
            if upperBound > len(results):
                upperBound = len(results) - 1
                quit = True
            string = str(lowerBound+1) + "-" + str(upperBound+1)
            resultArray = results[lowerBound:upperBound]
            self.resultPage.Append(string,resultArray)
            lowerBound = upperBound+1
            upperBound += self._maxResults
            upperBound += 1
        self.resultPage.SetStringSelection("1-"+str(self._maxResults))

    def searchDone(self,files):
        self.thread.join()
        dirs = []
        filesOnly = []
        tooBig = False
        for file in files:
            if file[AEnums.DIR]:
                dirs.append(file)
            else:
                filesOnly.append(file)
        results = filesOnly + dirs
        if len(results) > self._maxResults:
            tooBig = True
            self.paginateResults(results)
            results = results[0:self._maxResults]
        self.SetStatusText("Search complete! " + str(len(files)) + " Result(s) found.",1)
        self.results.setMode(easyListCtrl.FILE)
        self.updateResultLabel("Search Results")
        self.results.populateFiles(results,None,True)
        if tooBig:
            newItem = wx.ListItem()
            newItem.SetText("Over "+str(self._maxResults)+" search results.")
            newItem.SetTextColour(wx.RED)
            newItem.SetImage(self.fileUtilObj.getError())
            self.results.InsertItem(newItem)
        self.results.adjust()
        self.idle()

    def cancelAction(self):
        self.thread.stop()
        self.SetStatusText("Action cancelled!",1)
        self.idle()

    def threadedSearch(self,regex):
        results = self.buManager.searchFiles(regex)
        wx.CallAfter(self.searchDone,results)
        os.sys.exit(0)

    def refreshBackup(self,event):
        drives = self.buManager.getBackupDrives()
        self.thread = StoppableThread(self.bucket, self.threadedRefresh, drives)
        self.thread.start()
        self.busy("Refreshing Backup Drives")

    def refreshContent(self,event):
        drives = self.buManager.getContentDrives()
        self.thread = StoppableThread(self.bucket, self.threadedRefresh, drives)
        self.thread.start()
        self.busy("Refreshing Content Drives")

    def refreshSelected(self,event):
        drives = []
        driveRefs = self.drives.GetSelectedData()
        if driveRefs is None:
            return;
        self.thread = StoppableThread(self.bucket, self.threadedRefresh, driveRefs)
        self.thread.start()
        self.busy("Refreshing drive(s)...")

    def threadedRefresh(self,drives):
        errorMessages = []
        remote = False
        for driveRef in drives:
            if self.buManager.remoteReady(driveRef):
                answer = wx.MessageDialog(self,'Remote Index?','Do you want to remote index eligible drives?',style=wx.YES_NO|wx.ICON_QUESTION).ShowModal()
                if answer == wx.ID_YES:
                    remote = True
                break
        self.buManager.refresh(drives,remote)
        wx.CallAfter(self.refreshDone,errorMessages)

    def refreshDone(self,errorMessages):
        self.thread.join()
        self.idle()
        self.results.setMode(easyListCtrl.STRING)
        self.resultPage.Clear()
        self.results.populateStrings(errorMessages)
        if errorMessages:
            self.SetStatusText("Refresh completed with errors.",1)
        else:
            self.SetStatusText("Refresh completed.",1)
        self.updateResultLabel("Refresh Errors")
        self.typeChanged(None)

    def editLists(self,event):
        if self.running:
            return
        dialog = listdialog(self,self.buManager)
        dialog.ShowModal()

    def exit(self,event):
        #config = wx.Config('AnyBackup')
        #config.Write('layout',self.mgr.SavePerspective())
        if self.running:
            answer = wx.MessageBox("A background action is still self.running, quit anyway?","Potential data loss!",wx.OK | wx.CANCEL, self)
            if answer == wx.OK:
                os.sys.exit(0)
            else:
                return
        else:
            os.sys.exit(0)

    def busy(self,title = None):
        if not title:
            title = "Working..."
        self.running = True
        self.cursor = wx.BusyCursor()
        self.progress = ScrollingProgressDialog(self,title)
        self.progress.ShowModal()

    def idle(self):
        if self.progress is not None:
            self.progress.Close(1)
        while not self.errorAnswerQueue.empty():
            self.errorAnswerQueue.get_nowait()
        self.running = False
        self.cursor = None

    def backupDone(self,errorMessages):
        self.thread.join()
        self.idle()
        self.results.setMode(easyListCtrl.STRING)
        self.resultPage.Clear()
        self.results.populateStrings(errorMessages)
        if errorMessages:
            self.SetStatusText("Backup completed with errors.",1)
        else:
            self.SetStatusText("Backup completed.",1)
        self.updateResultLabel("Backup Errors")
        self.typeChanged(None)
        self.unsaved = True

    def restoreDone(self,errorMessages):
        self.thread.join()
        self.idle()
        self.results.setMode(easyListCtrl.STRING)
        self.resultPage.Clear()
        self.results.populateStrings(errorMessages)
        if errorMessages:
            self.SetStatusText("Restore completed with errors.",1)
        else:
            self.SetStatusText("Restore completed.",1)
        self.updateResultLabel("Restore Errors")
        self.typeChanged(None)
        self.unsaved = True

    def backupProgress(self,status):
        self.SetStatusText(status,1)

    def restoreProgress(self,status):
        self.SetStatusText(status,1)

    def missingDrive(self,id):
        return wx.MessageBox("Please connect drive "+id,"Hit cancel to skip this drive.",wx.OK | wx.CANCEL, self)==wx.OK

    def guiToBackup(self,event):
        if self.running:
            return
        self.SetStatusText("Generating list of files to backup...",1)
        self.thread = StoppableThread(self.bucket, self.threadedNewFiles)
        self.thread.start()
        self.busy("Generating Backup List")

    def guiDuplicates(self,event):
        if self.running:
            return
        self.SetStatusText("Generating list of pending duplicates...",1)
        self.thread = StoppableThread(self.bucket, self.threadedDuplicateFiles)
        self.thread.start()
        self.busy("Generating Pending Duplicate List")

    def guiOldFiles(self,event):
        if self.running:
            return
        self.SetStatusText("Generating list of old files...",1)
        self.thread = StoppableThread(self.bucket, self.threadedOldFiles)
        self.thread.start()
        self.busy("Generating Old File List")

    def toBackupDone(self,files):
        self.idle()
        self.thread.join()
        self.results.setMode(easyListCtrl.FILE)
        self.resultPage.Clear()
        total = len(files)
        tooBig = False
        if len(files) > self._maxResults:
            tooBig = True
            self.paginateResults(files)
            files = files[0:self._maxResults]
        self.results.populateFiles(files,None,True)
        if tooBig:
            newItem = wx.ListItem()
            newItem.SetText("Over "+str(self._maxResults)+" files found, clipping results.")
            newItem.SetTextColour(wx.RED)
            newItem.SetImage(self.fileUtilObj.getError())
            self.results.InsertItem(newItem)
        self.results.adjust()
        self.updateResultLabel("Files To Backup")
        self.SetStatusText("Backup list generated -- "+str(total)+" files found.",1)

    def duplicatesDone(self,files):
        self.idle()
        self.thread.join()
        self.results.setMode(easyListCtrl.FILE)
        self.resultPage.Clear()
        total = len(files)
        tooBig = False
        if len(files) > self._maxResults:
            tooBig = True
            self.paginateResults(files)
            files = files[0:self._maxResults]
        self.results.populateFiles(files,None,True)
        if tooBig:
            newItem = wx.ListItem()
            newItem.SetText("Over "+str(self._maxResults)+" files found, clipping results.")
            newItem.SetTextColour(wx.RED)
            newItem.SetImage(self.fileUtilObj.getError())
            self.results.InsertItem(newItem)
        self.results.adjust()
        self.updateResultLabel("Duplicate Files")
        self.SetStatusText("Duplicate file list generated -- "+str(total)+" files found.",1)


    def oldFilesDone(self,files):
        self.idle()
        self.thread.join()
        self.results.setMode(easyListCtrl.FILE)
        self.resultPage.Clear()
        total = len(files)
        tooBig = False
        if len(files) > self._maxResults:
            tooBig = True
            self.paginateResults(files)
            files = files[0:self._maxResults]
        self.results.populateFiles(files,None,True)
        if tooBig:
            newItem = wx.ListItem()
            newItem.SetText("Over "+str(self._maxResults)+" files found, clipping results.")
            newItem.SetTextColour(wx.RED)
            newItem.SetImage(self.fileUtilObj.getError())
            self.results.InsertItem(newItem)
        self.results.adjust()
        self.updateResultLabel("Old Files")
        self.SetStatusText("Old file list generated -- "+str(total)+" files found.",1)

    def threadedOldFiles(self):
        files = self.buManager.getOldFiles()
        wx.CallAfter(self.oldFilesDone,files)
        os.sys.exit(0)

    def threadedDuplicateFiles(self):
        files = self.buManager.getDuplicates()
        wx.CallAfter(self.duplicatesDone,files)
        os.sys.exit(0)

    def threadedNewFiles(self):
        files = self.buManager.getNewFiles()
        wx.CallAfter(self.toBackupDone,files)
        os.sys.exit(0)

    def guiBackup(self,event):
        if self.running:
            return
        totalSize = 0
        self.SetStatusText("Backing up new files...",1)
        self.thread = StoppableThread(self.bucket, self.threadedBackup)
        self.thread.start()
        self.busy("Backing Up New Files")

    def guiRestore(self,event):
        if self.running:
            return
        self.SetStatusText("Restoring missing files...",1)
        self.thread = StoppableThread(self.bucket, self.threadedRestore)
        self.thread.start()
        self.busy("Restoring Missing Files")

    def threadedRestore(self):
        errorMessages = self.buManager.restoreOldFiles()
        wx.CallAfter(self.restoreDone,errorMessages)
        os.sys.exit(0)

    def threadedBackup(self):
        errorMessages = self.buManager.backupNewFiles()
        wx.CallAfter(self.backupDone,errorMessages)

    def guiAddBackupDrive(self,event):
        self.guiAddDrive(event,AEnums.BACKUP)

    def guiAddContentDrive(self,event):
        self.guiAddDrive(event,AEnums.CONTENT)

    def guiAddDrive(self,event,driveType=None):
        if not driveType:
            driveType = AEnums.BACKUP
        if self.running:
            return
        drives = DriveUtil.getLetters()
        lst = drives.keys()
        if driveType == AEnums.BACKUP:
            dialog = wx.MultiChoiceDialog(self,"Choose a drive","Choose",lst)
            if dialog.ShowModal() != wx.ID_OK:
                return
            choices = dialog.GetSelections()
            if not choices:
                return
            strings = []
            for choice in choices:
                strings.append(drives[lst[choice]])
        else:
            dialog = MultiDirDialog(self,'Choose directories to add for content drives.','Choose content directories')
            if dialog.ShowModal() != wx.ID_OK:
                return
            choices = dialog.GetPaths()
            if not choices:
                return
            strings = []
            for choice in choices:
                strings.append(choice)
        indexNow = False
        answer = wx.MessageDialog(self,'Index drive(s) now?','OK to index, cancel to defer',style=wx.YES_NO|wx.ICON_QUESTION).ShowModal()
        if answer == wx.ID_YES:
            indexNow = True
        self.SetStatusText("Adding and indexing drive(s)...",1)
        if self.buManager.getSetName() in self.sdialog:
            del self.sdialog[self.buManager.getSetName()]
        self.thread = StoppableThread(self.bucket, self.threadedAdd,strings,indexNow,driveType)
        self.thread.start()
        self.busy("Adding %d drives..."%len(strings))

    def guiRemoveDrive(self,event):
        if self.running:
            return
        driveRef = None
        self.running = True
        driveRefs = self.drives.GetSelectedData()
        if not driveRefs:
            self.running = False
            return
        self.buManager.removeDrives(driveRefs)
        self.drives.DeleteAllItems()
        self.contents.DeleteAllItems()
        self.dirTreeObj.DeleteAllItems()
        if self.buManager.getSetName() in self.sdialog:
            del self.sdialog[self.buManager.getSetName()]
        drives = self.getDrives()
        self.updateFileLabel("")
        if drives:
            self.drives.populateDrives(drives)

        self.idle()
        self.unsaved = True

    def threadedAdd(self, strings, index, driveType):
        errorMessages = []
        if driveType == AEnums.BACKUP:
            for letter in strings:
                result = self.buManager.addBackupDrive(letter,index)
                if result == AEnums.DUPLICATE:
                    errorMessages.append("Drive at " + letter + ": has already been added!")
                elif result == AEnums.UNNAMED:
                    errorMessages.append("Drive at " + letter + ": is unnamed, you must define a volume name before adding a drive to AnyBackup!")
        else:
            drives = {}
            for dir in strings:
                (letter,path) = DriveUtil.explodePath(dir)
                if not letter in drives:
                    drives[letter] = []
                drives[letter].append(path)
            for letter in drives.keys():
                result = self.buManager.addContentDrive(letter,drives[letter],index)
                if result == AEnums.UNNAMED:
                    wx.MessageDialog(None, 'Drives must have a volume name to be added!', 'Unnamed Drive', wx.OK | wx.ICON_ERROR).ShowModal()
        wx.CallAfter(self.done,errorMessages)
        os.sys.exit(0)

    def resultFileClicked(self,event):
        if self.running:
            return
        if self.results.isAutoSelect():
            return
        fileRef = self.results.GetSelectedData()
        if not fileRef:
            return
        else:
            fileRef = fileRef[0]
        if not fileRef[AEnums.DIR] and not AEnums.PARENT in fileRef:
            return
        driveRef = self.buManager.getDrive(fileRef)
        if not driveRef:
            return
        self.dirTreeObj.populate(driveRef)
        if fileRef[AEnums.DIR]:
            self.updateFileLabel(fileRef[AEnums.PREFIX]+os.sep+fileRef[AEnums.PATH]+os.sep+fileRef[AEnums.NAME])
            parent = fileRef.get(AEnums.PARENT,None)
            files = self.buManager.getFiles(fileRef[AEnums.ID])
        else:
            self.updateFileLabel(fileRef[AEnums.PREFIX]+os.sep+fileRef[AEnums.PATH])
            parent = fileRef.get(AEnums.PARENT,None)
            files = self.buManager.getFiles(parent)
        files = self.buManager.sortFiles(files)
        files.reverse()
        self.contents.DeleteAllItems()
        if not fileRef[AEnums.DIR]:
            p = self.buManager.getFile(parent)
            self.contents.populateFiles(files,p)
        else:
            self.contents.populateFiles(files,fileRef)

    def fileClicked(self,event):
        if self.running:
            return
        if self.contents.isAutoSelect():
            return
        fileRef = self.contents.GetData(event.GetItem())
        if fileRef[AEnums.DIR]:
            self.contents.DeleteAllItems()
            files = self.buManager.getFiles(fileRef[AEnums.ID])
            files = self.buManager.sortFiles(files)
            self.updateFileLabel(fileRef[AEnums.PREFIX]+os.sep+fileRef[AEnums.PATH] + os.sep + fileRef[AEnums.NAME])
            files.reverse()
            self.contents.populateFiles(files,fileRef)

    def done(self,errorMessages):
        self.idle()
        drives = []
        self.results.setMode(easyListCtrl.STRING)
        self.resultPage.Clear()
        self.results.populateStrings(errorMessages)
        self.refreshDrives()
        self.updateFileLabel("Files")
        self.SetStatusText("",1)
        self.unsaved = True

    def refreshDrives(self):
        drives = self.getDrives()
        self.drives.DeleteAllItems()
        self.contents.DeleteAllItems()
        self.results.DeleteAllItems()
        self.dirTreeObj.DeleteAllItems()
        self.SetStatusText("",1)
        self.drives.populateDrives(drives)

    def typeChanged(self,event):
        if self.running:
            return
        drives = self.getDrives()
        self.dirTreeObj.DeleteAllItems()
        self.drives.DeleteAllItems()
        self.contents.DeleteAllItems()
        self.updateFileLabel("Files")
        self.drives.populateDrives(drives)

    def treeExpanded(self,event):
        item = event.GetItem()
        fileRef = self.dirTreeObj.GetItemData(item).GetData()
        files = self.buManager.getFiles(fileRef)
        files = self.buManager.sortFiles(files)
        dirs = [x for x in files if x[AEnums.DIR]]
        if dirs and self.dirTreeObj.GetChildrenCount(item) <= 0:
            self.dirTreeObj.populateTree(item,dirs)
            self.dirTreeObj.Expand(item)
        elif not dirs:
            self.dirTreeObj.SetItemHasChildren(item,False)
        #if self.dirTreeObj.ItemHasChildren(item) and self.dirTreeObj.GetChildrenCount(item) <= 0:
        #
        #

    def updateFileLabel(self,string):
        paneInfo = self.mgr.GetPane(self.contents.getListCtrl())
        paneInfo.Caption(unicode(string).encode('utf-8'))
        self.mgr.Update()

    def updateResultLabel(self,string):
        paneInfo = self.mgr.GetPane('Results')
        paneInfo.Caption(string)
        self.mgr.Update()

    def treeClicked(self,event):
        item = event.GetItem()
        fileRef = self.dirTreeObj.GetItemData(item).GetData()
        self.contents.DeleteAllItems()
        files = self.buManager.getFiles(fileRef)
        files = self.buManager.sortFiles(files)
        dirs = [x for x in files if x[AEnums.DIR]]
        if dirs and self.dirTreeObj.GetChildrenCount(item) <= 0:
            self.dirTreeObj.populateTree(item,dirs)
            self.dirTreeObj.Expand(item)
        elif not dirs:
            self.dirTreeObj.SetItemHasChildren(item,False)
        try:
            self.updateFileLabel(fileRef[AEnums.PREFIX]+os.sep+fileRef[AEnums.PATH] + os.sep + fileRef[AEnums.NAME])
        except KeyError:
            d = fileRef
            self.updateFileLabel(d[AEnums.NAME]+":"+os.sep)
        files.reverse()
        self.contents.populateFiles(files,fileRef)

    def driveClicked(self,event):
        if self.running:
            return
        if self.drives.isAutoSelect():
            return
        driveRef = self.drives.GetData(event.GetItem())
        if not driveRef:
            return
        self.dirTreeObj.populate(driveRef)
        self.contents.DeleteAllItems()
        rootFile = self.buManager.getRootFile(driveRef)
        if not rootFile:
            return
        #files = self.buManager.getFiles(rootFile)
        files = self.buManager.sortFiles(rootFile)
        self.updateFileLabel(driveRef[AEnums.NAME]+":\\")
        files.reverse()
        self.contents.populateFiles(files)
